import type { ElementSelectors } from '../../lib/abstract'
import type { ComponentStyleIdType } from '../../types/interfaces'
import type { ScrollButtonProps } from './types'
import { UIComponent } from '../../lib/abstract'
import { BodyHandler } from '../../lib/body_handler'
import { IconService } from '../../lib/icons'
import { ICON_IDS } from '../../lib/icons/types/constants'
import { ComponentStyleId } from '../../types/constants'
import { styles } from './styles.ts'
import { DEFAULT_SCROLL_PROPS, ScrollDirection } from './types'

export class ScrollButton extends UIComponent<ScrollButtonProps> {
  id: string = 'scroll-button'
  readonly description: string = '页面滚动按钮'
  readonly idClass: string = 'custom'
  readonly styleId: string = ComponentStyleId[this.name]
  private lastScrollTime = 0
  isScrollable = false

  get name(): ComponentStyleIdType {
    return 'ScrollButton'
  }

  get className(): string {
    return `${this.props.id}-${this.idClass}`
  }

  constructor(props: ScrollButtonProps) {
    super({
      ...DEFAULT_SCROLL_PROPS,
      ...props,
    })

    // 初始化时检查页面是否需要滚动
    this.isScrollable = this.checkScrollNeeded()

    // 注册样式
    this.registerStyles()
  }

  ELEMENT_SELECTORS: ElementSelectors = {
    button: { selector: '.scroll-button', dynamic: false, unique: false },
  }

  render(): string {
    const { forceShow } = this.props as ScrollButtonProps

    if (!forceShow && !this.isScrollable)
      return ''

    const { direction } = this.props as ScrollButtonProps
    return `
      <div class="${this.className} ${this.idClass}">
        <button 
          class="scroll-button" 
          data-direction="${direction}"
          title="${direction === ScrollDirection.Top ? '返回顶部' : '直达底部'}"
          aria-label="${direction === ScrollDirection.Top ? '返回页面顶部' : '跳转至页面底部'}"
        >
          ${IconService.getInstance().api.icon.asSvg(ICON_IDS.other.scroll)}
        </button>
      </div>
    `
  }

  /**
   * 检查页面是否需要滚动功能
   */
  private checkScrollNeeded(threshold = 1.2, buffer = 10): boolean {
    // 1. 通过获取 HTML 文档根元素（<html>元素）所有最终计算样式，排除 CSS 强制禁用滚动的情况
    const style = getComputedStyle(document.documentElement)

    if (style.overflow === 'hidden' || style.overflow === 'clip') {
      return false
    }

    // 2. 获取精确高度
    const docHeight = Math.max(
      document.body.scrollHeight,
      document.documentElement.scrollHeight,
    )

    const winHeight = window.innerHeight || document.documentElement.clientHeight

    // 3. 增加缓冲避免小数误差
    return (docHeight > winHeight * threshold + buffer) && BodyHandler.checkScrollable()
  }

  mount(container: JQuery<HTMLElement>): void {
    const { forceShow } = this.props as ScrollButtonProps

    if (!forceShow && !this.isScrollable)
      return

    super.mount(container)
  }

  bindEvents(): void {
    const { offset = 0 } = this.props as ScrollButtonProps

    this.getElement('button')?.on('click', () => {
      const scrollTo = this.getScrollTargetPosition()

      $('html, body').animate({ scrollTop: scrollTo + offset }, 500, () => {
        // 滚动完成后重新检测位置
        setTimeout(() => {
          this.lastScrollTime = 0 // 重置防抖
          this.handleScrollDirection()
        }, 600)
      })
    })

    // 滚动方向自动检测
    $(window).on('scroll', this.handleScrollDirection.bind(this))
    this.handleScrollDirection() // 初始化检测
  }

  private getScrollTargetPosition(): number {
    const currentDir = this.getElement('button')?.attr('data-direction') || ScrollDirection.Top
    return currentDir === ScrollDirection.Top
      ? 0
      : $(document).height()! - $(window).height()!
  }

  private handleScrollDirection(): void {
    const now = Date.now()

    // 防抖：100ms内不重复处理
    if (now - this.lastScrollTime < 100)
      return

    this.lastScrollTime = now

    const $button = this.getElement('button')

    if (!$button)
      return

    // 页面顶部被卷起的高度
    const scrollTop = $(window).scrollTop() || 0
    // 窗口高度
    const windowHeight = $(window).height() || 0
    // 文档高度
    const docHeight = $(document).height() || 0

    // 计算可视区域边界（添加10px缓冲避免边界抖动）
    const buffer = 10
    const bottomThreshold = Math.max(0, docHeight - windowHeight - buffer)

    // 新的区域划分（互斥判断）
    const isNearTop = scrollTop < buffer
    const isNearBottom = scrollTop > bottomThreshold

    // 获取按钮当前记录的滚动方向
    const currentDir = $button.attr('data-direction') || ScrollDirection.Top
    let newDir = currentDir as ScrollDirection

    if (isNearTop) {
      newDir = ScrollDirection.Bottom // 在顶部时按钮应指向底部
    }
    else if (isNearBottom) {
      newDir = ScrollDirection.Top // 在底部时按钮应指回顶部
    }
    // 中间区域保持当前方向

    // 如果方向发生变化，则更新按钮状态
    if (newDir && newDir !== currentDir) {
      this.setDirection(newDir!)
    }
  }

  setDirection(direction: ScrollDirection): void {
    const $button = this.getElement('button')

    if (!$button)
      return

    $button
      .attr('title', direction === ScrollDirection.Top ? '返回顶部' : '直达底部')
      .attr('aria-label', direction === ScrollDirection.Top ? '返回页面顶部' : '跳转至页面底部')
      .attr('data-direction', direction)
      .toggleClass('rotated', direction === ScrollDirection.Bottom)
  }

  removeEventListeners(): void {
    this.getElement('button')?.off('click')
    $(window).off('scroll', this.handleScrollDirection)
  }

  cleanupReferences(): void {
    this.lastScrollTime = 0
    this.getElement('button')?.closest(this.selector).remove()
  }

  getStyles(): string {
    return styles(this.className)
  }
}
